package owl2vowl.model.data;

import owl2vowl.constants.PropertyAllSomeValue;
import owl2vowl.constants.VowlAttribute;
import owl2vowl.model.annotation.Annotation;
import owl2vowl.model.data.VowlData;
import owl2vowl.model.data.VowlGenerationEnum;
import owl2vowl.model.entities.AbstractEntity;
import owl2vowl.model.entities.nodes.classes.VowlClass;
import owl2vowl.model.entities.nodes.classes.VowlThing;
import owl2vowl.model.entities.nodes.datatypes.DatatypeReference;
import owl2vowl.model.entities.nodes.datatypes.LiteralReference;
import owl2vowl.model.entities.nodes.datatypes.VowlLiteral;
import owl2vowl.model.entities.properties.AbstractProperty;
import owl2vowl.model.entities.properties.DatatypeValueReference;
import owl2vowl.model.entities.properties.ObjectValueReference;
import owl2vowl.model.entities.properties.TypeOfProperty;
import owl2vowl.model.entities.properties.VowlObjectProperty;
import owl2vowl.model.individuals.VowlIndividual;
import java.util.Collection;
import java.util.function.Consumer;
import org.semanticweb.owlapi.model.IRI;

public class VowlGenerator {
	private final VowlData vowlData;

	public VowlGenerator(VowlData vowlData) {
		this.vowlData = vowlData;
	}

	protected void applyAnonymous(AbstractEntity entity) {
		entity.setGenerated(VowlGenerationEnum.MANUALLY);
		entity.addAttribute(VowlAttribute.ANONYMOUS);
	}

	public VowlClass generateIntersection(Collection<IRI> iriList) {
		VowlClass element = new VowlClass(this.vowlData.getNewIri());
		this.applyAnonymous(element);
		iriList.stream().forEach(element::addElementToIntersection);
		element.addAttribute(VowlAttribute.INTERSECTION);
		this.vowlData.addClass(element);
		return element;
	}

	public VowlClass generateUnion(Collection<IRI> iriList) {
		VowlClass element = new VowlClass(this.vowlData.getNewIri());
		this.applyAnonymous(element);
		iriList.stream().forEach(element::addElementToUnion);
		element.addAttribute(VowlAttribute.UNION);
		this.vowlData.addClass(element);
		return element;
	}

	public VowlClass generateComplement(IRI iri) {
		VowlClass element = new VowlClass(this.vowlData.getNewIri());
		this.applyAnonymous(element);
		element.addComplement(iri);
		element.addAttribute(VowlAttribute.COMPLEMENT);
		this.vowlData.addClass(element);
		return element;
	}

	public VowlClass generateAnonymousClass() {
		VowlClass element = new VowlClass(this.vowlData.getNewIri());
		this.applyAnonymous(element);
		this.vowlData.addClass(element);
		return element;
	}

	public VowlThing generateThing() {
		VowlThing thing = new VowlThing(this.vowlData.getNewIri());
		thing.setGenerated(VowlGenerationEnum.MANUALLY);
		this.vowlData.addClass(thing);
		return thing;
	}

	public AbstractProperty generateDisjointProperty(IRI domain, IRI range) {
		if (domain != null && range != null) {
			VowlObjectProperty property = new VowlObjectProperty(this.vowlData.getNewIri());
			this.applyAnonymous(property);
			property.addDomain(domain);
			property.addRange(range);
			property.setType("owl:disjointWith");
			this.vowlData.addObjectProperty(property);
			return property;
		} else {
			throw new IllegalArgumentException("Parameters should not be null!");
		}
	}

	public AbstractProperty generateSubclassProperty(IRI domain, IRI range) {
		if (domain != null && range != null) {
			VowlObjectProperty property = new VowlObjectProperty(this.vowlData.getNewIri());
			this.applyAnonymous(property);
			property.addDomain(domain);
			property.addRange(range);
			property.setType("rdfs:SubClassOf");
			property.getAnnotations().addLabel(new Annotation("label", "SubClassOf"));
			this.vowlData.addObjectProperty(property);
			return property;
		} else {
			throw new IllegalArgumentException("Parameters should not be null!");
		}
	}

	public VowlIndividual generateIndividual(IRI iri) {
		VowlIndividual individual = new VowlIndividual(iri);
		this.vowlData.addIndividual(individual);
		return individual;
	}

	public TypeOfProperty generateTypeOf(IRI domain, IRI range) {
		if (domain != null && range != null) {
			TypeOfProperty property = new TypeOfProperty(this.vowlData.getNewIri());
			property.setGenerated(VowlGenerationEnum.MANUALLY);
			property.addDomain(domain);
			property.addRange(range);
			this.vowlData.addTypeOfProperty(property);
			return property;
		} else {
			throw new IllegalArgumentException("Parameters should not be null!");
		}
	}

	public DatatypeReference generateDatatypeReference(IRI reference) {
		if (reference == null) {
			throw new IllegalArgumentException("Parameters should not be null!");
		} else {
			DatatypeReference reference1 = new DatatypeReference(this.vowlData.getNewIri(), reference);
			reference1.setGenerated(VowlGenerationEnum.MANUALLY);
			this.vowlData.addDatatype(reference1);
			return reference1;
		}
	}

	public ObjectValueReference generateObjectValueReference(IRI reference, PropertyAllSomeValue value) {
		if (reference == null) {
			throw new IllegalArgumentException("Parameters should not be null!");
		} else {
			ObjectValueReference reference1 = new ObjectValueReference(this.vowlData.getNewIri(), reference);
			reference1.setGenerated(VowlGenerationEnum.MANUALLY);
			reference1.setValue(value);
			this.vowlData.addObjectProperty(reference1);
			this.vowlData.getPropertyForIri(reference).addReferencedProperty(reference1.getIri());
			return reference1;
		}
	}

	public DatatypeValueReference generateDatatypeValueReference(IRI reference, PropertyAllSomeValue value) {
		if (reference == null) {
			throw new IllegalArgumentException("Parameters should not be null!");
		} else {
			DatatypeValueReference reference1 = new DatatypeValueReference(this.vowlData.getNewIri(), reference);
			reference1.setGenerated(VowlGenerationEnum.MANUALLY);
			reference1.setValue(value);
			this.vowlData.addDatatypeProperty(reference1);
			this.vowlData.getPropertyForIri(reference).addReferencedProperty(reference1.getIri());
			return reference1;
		}
	}

	public VowlLiteral generateLiteral() {
		LiteralReference vowlLiteral = new LiteralReference(this.vowlData.getNewIri(),
				this.vowlData.getGenericLiteral().getIri());
		vowlLiteral.setGenerated(VowlGenerationEnum.MANUALLY);
		this.vowlData.addDatatype(vowlLiteral);
		return vowlLiteral;
	}
}